package de.mmis.core.tuplespace;

import java.util.Map;

import de.mmis.core.base.DeserializableAnnotations;
import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.abstracttree.serializer.Serializable;
import de.mmis.core.base.event.AbstractTypedEvent;
import de.mmis.core.tuplespace.Notification.Command;

/**
 * This is the super class of all events TupleSpaces fire.
 * 
 * @author Martin Nyolt
 * 
 */
public class TuplespaceEvent extends AbstractTypedEvent<TuplespaceEvent.TYPE>
		implements Serializable, DeserializableAnnotations {

	private Tree template;
	private Command notifyCmd;

	public TuplespaceEvent(TYPE type) {
		super(type);
	}

	public static enum TYPE {
		NOTIFICATION
	}

	public static TuplespaceEvent newNotification(Command cmd, Tree template) {
		TuplespaceEvent e = new TuplespaceEvent(TYPE.NOTIFICATION);
		e.notifyCmd = cmd;
		e.template = template;
		return e;
	}

	/**
	 * @return the template of the notification, iff this event is a
	 *         notification. <b>null</b> otherwise.
	 */
	public Tree getTemplate() {
		return template;
	}

	public Command getNotificationCommand() {
		return notifyCmd;
	}

	@Override
	public Tree[] serialize(boolean explicit, Map<Object, String> refmap,
			ClassLoader classLoader) {
		Tree specialSerialise = null;

		switch (getEventType()) {
		case NOTIFICATION:
			specialSerialise = new InnerNode(Tree.fromObject(notifyCmd, false,
					refmap, classLoader), template);
			break;
		}

		return new Tree[] {
				Tree.fromObject(getEventType(), false, refmap, classLoader),
				specialSerialise };
	}

	@Override
	public String getTypeIdentifier() {
		return "tuplespace-event";
	}

	@DeserializationMethod
	public static Object deserialize(Tree[] e, ClassLoader classLoader,
			String ownID, Map<String, Object> idmap)
			throws DeserializableException {

		if (e.length != 2)
			throw new DeserializableException(new InnerNode(e),
					"tuplespace-event must have 2 parameters");

		switch (e[0].getAs(TuplespaceEvent.TYPE.class)) {
		case NOTIFICATION:
			return deserialiseNotification(e[1]);
		default:
			throw new DeserializableException(e[0], "Type not found");
		}
	}

	private static TuplespaceEvent deserialiseNotification(Tree e)
			throws DeserializableException {
		if (!(e instanceof InnerNode))
			throw new DeserializableException(e, "SExpression must be a Struct");

		InnerNode exp = (InnerNode) e;
		if (exp.getNumberOfSubTrees() != 2)
			throw new DeserializableException(exp,
					"Struct must have 2 Subexpressions");

		Tree sub0 = exp.getSubTree(0);
		Command command = sub0.getAs(Command.class);

		return newNotification(command, exp.getSubTree(1));
	}

	@Override
	public String toString() {
		return "TuplespaceEvent [notifyCmd=" + notifyCmd + ", template="
				+ template + ", eventType=" + getEventType() + "]";
	}

}
